

#include <string.h>
#include "interrupt.h"
#include "sdkconfig.h"
#include "_cpu.h"
#include "parameters.h"
#ifdef CONFIG_USE_GIC
#ifdef CONFIG_EBABLE_GICV3
#include "gicv3.h"
#else
#include "gic.h"
#endif
#endif

#define MAX_HANDLERS 1024

/* exception and interrupt handler table */
struct IrqDesc isr_table[MAX_HANDLERS];


const unsigned int VECTOR_BASE = 0x00;

#ifdef RT_USING_GIC_V2
/**
 * This function will initialize hardware interrupt
 */
void InterruptInit(u32 gic_dist_base, u32 gic_irq_start)
{
    u32 gic_irq_start;

    /* initialize vector table */
    // rt_hw_vector_init();

    /* initialize exceptions table */
    memset(isr_table, 0x00, sizeof(isr_table));

    /* initialize ARM GIC */

    gic_irq_start = GIC_IRQ_START;

    arm_gic_dist_init(0, gic_dist_base, gic_irq_start);
    arm_gic_cpu_init(0, gic_cpu_base);
}
#else
/**
 * This function will initialize hardware interrupt
 * Called by the primary cpu(cpu0)
 */
void InterruptInit(u32 gic_dist_base, u32 gic_irq_start)
{

    /* initialize exceptions table */
    memset(isr_table, 0x00, sizeof(isr_table));

    /* per cpu core need to run */
    ArmGicDistInit(0, gic_dist_base, gic_irq_start);

    ArmGicCpuInit(0);

    ArmGicRedistInit(0);
}

#endif

/**
 * This function will mask a interrupt.
 * @param vector the interrupt number
 */
void InterruptMask(int vector)
{
    ArmGicMask(0, vector);
}

/**
 * This function will un-mask a interrupt.
 * @param vector the interrupt number
 */
void InterruptUmask(int vector)
{
    ArmGicUmask(0, vector);
}

/**
 * This function returns the active interrupt number.
 * @param none
 */
int InterruptGetIrq(void)
{
    return ArmGicGetActiveIrq(0);
}

/**
 * This function acknowledges the interrupt.
 * @param vector the interrupt number
 */
void InterruptAck(int vector)
{
    ArmGicAck(0, vector);
}

/**
 * This function set interrupt CPU targets.
 * @param vector:   the interrupt number
 *        cpu_mask: target cpus mask, one bit for one core
 */
void InterruptSetTargetCpus(int vector, unsigned int cpu_mask)
{
    ArmGicSetCpu(0, vector, cpu_mask);
}

/**
 * This function get interrupt CPU targets.
 * @param vector: the interrupt number
 * @return target cpus mask, one bit for one core
 */
unsigned int InterruptGetTargetCpus(int vector)
{
    return ArmGicGetTargetCpu(0, vector);
}

/**
 * This function set interrupt triger mode.
 * @param vector: the interrupt number
 *        mode:   interrupt triger mode; 0: level triger, 1: edge triger
 */
void InterruptSetTrigerMode(int vector, unsigned int mode)
{
    ArmGicSetConfiguration(0, vector, mode);
}

/**
 * This function get interrupt triger mode.
 * @param vector: the interrupt number
 * @return interrupt triger mode; 0: level triger, 1: edge triger
 */
unsigned int InterruptGetTrigerMode(int vector)
{
    return ArmGicGetConfiguration(0, vector);
}

/**
 * This function set interrupt pending flag.
 * @param vector: the interrupt number
 */
void InterruptSetPending(int vector)
{
    ArmGicSetPendingIrq(0, vector);
}

/**
 * This function get interrupt pending flag.
 * @param vector: the interrupt number
 * @return interrupt pending flag, 0: not pending; 1: pending
 */
unsigned int InterruptGetPending(int vector)
{
    return ArmGicGetPendingIrq(0, vector);
}

/**
 * This function clear interrupt pending flag.
 * @param vector: the interrupt number
 */
void InterruptClearPending(int vector)
{
    // arm_gic_clear_pending_irq(0, vector);
    ArmGicClearPendingIrq(0, vector);
}

/**
 * This function set interrupt priority value.
 * @param vector: the interrupt number
 *        priority: the priority of interrupt to set
 */
void InterruptSetPriority(int vector, unsigned int priority)
{
    // arm_gic_set_priority(0, vector, priority);
    ArmGicSetPriority(0, vector, priority);
}

/**
 * This function get interrupt priority.
 * @param vector: the interrupt number
 * @return interrupt priority value
 */
unsigned int InterruptGetPriority(int vector)
{
    // return arm_gic_get_priority(0, vector);
    return ArmGicGetPriority(0, vector);
}

/**
 * This function set priority masking threshold.
 * @param priority: priority masking threshold
 */
void InterruptSetPriorityMask(unsigned int priority)
{
    ArmGicSetInterfacePriorMask(0, priority);
}

/**
 * This function get priority masking threshold.
 * @param none
 * @return priority masking threshold
 */
unsigned int InterruptGetPriorityMask(void)
{
    return ArmGicGetInterfacePriorMask(0);
}

/**
 * This function set priority grouping field split point.
 * @param bits: priority grouping field split point
 * @return 0: success; -1: failed
 */
int InterruptSetPriorGroupBits(unsigned int bits)
{
    int status;

    if (bits < 8)
    {
        ArmGicSetBinaryPoint(0, (7 - bits));
        status = 0;
    }
    else
    {
        status = -1;
    }

    return (status);
}

/**
 * This function get priority grouping field split point.
 * @param none
 * @return priority grouping field split point
 */
unsigned int InterruptGetPriorGroupBits(void)
{
    unsigned int bp;

    bp = ArmGicGetBinaryPoint(0) & 0x07;

    return (7 - bp);
}

/**
 * This function will install a interrupt service routine to a interrupt.
 * @param vector the interrupt number
 * @param new_handler the interrupt service routine to be installed
 * @param old_handler the old interrupt service routine
 */
IrqHandler InterruptInstall(int vector, IrqHandler handler,
                            void *param, const char *name)
{
    IrqHandler old_handler = NULL;
    (void)name;
    if (vector < MAX_HANDLERS)
    {
        old_handler = isr_table[vector].handler;

        if (handler != NULL)
        {
            isr_table[vector].handler = handler;
            isr_table[vector].param = param;
        }
    }

    return old_handler;
}


void IpiSend(int ipi_vector, unsigned int cpu_mask)
{
#ifdef RT_USING_GIC_V2
    arm_gic_send_sgi(0, ipi_vector, cpu_mask, 0);
#else
    ArmGicSendAffinitySgi(0, ipi_vector, cpu_mask, ROUTED_TO_SPEC);
#endif
}

void IpiHandlerInstall(int ipi_vector, IrqHandler ipi_isr_handler)
{
    /* note: ipi_vector maybe different with irq_vector */
    InterruptInstall(ipi_vector, ipi_isr_handler, 0, "IPI_HANDLER");
}



void InterruptEarlyInit(void){
    u32 cpu_id ;
    cpu_id = GetCpuId() ;
    ArmGicRedistAddressSet(0, GICV3_RD_BASEADDRESS + cpu_id * GICV3_RD_OFFSET, cpu_id);
    if(0 == cpu_id){
		InterruptInit(GICV3_BASEADDRESS, 0);
	}else{
		ArmGicCpuInit(0);
		ArmGicRedistInit(0);
	}
}
